﻿using System;
using System.Xml;
using System.Data;
using System.Text;
using System.Configuration;
using System.Security.Principal;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Registry;
using SdmxMl.Manager;

namespace SdmxMl.Multiplexor
{
    #region Registry connection status

    public enum RepositoryState
    {
        Ok,
        Unused,
        OnError
    }
    #endregion

    /// <summary> Proxy Class to access TinyRegistry </summary>
    public class RegProxy
    {
        #region Properties

        /// <summary> NSI user ID in SMS system</summary>
        public string UserId { get; private set; }
        public string LastError { get; private set; }

        /// <summary>Current state of connexion</summary>
        public RepositoryState State { get; set; }

        public bool UserIsAdmin { get; set; }
        public string Url { get { return _url; } }

        // Version of Web service used
        public  double Version { get; set; }

        public bool BackupAutomatic { get; set; }

        #endregion


        #region Internal Members

        private SdmxMl.TinyRegistry.Repository smsProxy;
        private string VS;
        private string _url;

        #endregion // Internal Members

        #region C'tor

        // Constructor
        public RegProxy(string url, string vs, string userId)
        {
            LastError = string.Empty;

            try
            {
                UserId = userId;
                VS = vs;
                _url = url;

                Test();
            }
            catch 
            {
                State = RepositoryState.OnError;
            }
        }
        #endregion

        public RepositoryState Test()
        {
            try
            {
                LastError = string.Empty;
                smsProxy = new SdmxMl.TinyRegistry.Repository();
                smsProxy.Url = _url;

                //let's do an access and Get version of WS used
                Version = 0;
                StatusMessageType statusMsg = new StatusMessageType();
                statusMsg = new StatusMessageType(smsProxy.Test());
                if (statusMsg.IsStatusMessage == true)
                {
                    string versionLongue = statusMsg.Message.GetFirst();
                    string parseStringPass = versionLongue.Split(':')[1];
                    string[] parts = parseStringPass.Trim().Split('.');

                    Version = Convert.ToDouble(parts[0] + "." + parts[1]);
                }

                #region DEC 10.08.2011 : Backup version management
                BackupAutomatic = false;
                UserIsAdmin = false;
                if (Version >= 1.0)
                {
                    BackupAutomatic = smsProxy.BackupAutomaticEnable();
                    UserIsAdmin = IsAdministrator();
                }

                State = RepositoryState.Ok;

                #endregion
            }
            catch (Exception ex)
            {
                LastError = ex.Message;
                State = RepositoryState.OnError;
            }
            return State;
        }
  


        #region Public access to Registry service methods

        public string GetLastVersion(string arType, string fullIdent)
        {
            XmlDocument xDoc = null;
            string lastVers = "1.0";
            string[] arr = fullIdent.Split('+');
            if (arr.Length == 3)
                lastVers = arr[2];

            if (State == RepositoryState.Ok && Version >= 1.5)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.GetVersionEvolution(arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                    if (statusMsg.IsStatusMessage == false)
                    {
                        xDoc = new XmlDocument();
                        xDoc.LoadXml(el.OuterXml);
                        // we get a codelist with all versions as code
                        SmManager sm = new SmManager();
                        sm.LoadSdmxDocument(null, true, xDoc);
                        CodeListType clt = sm.GetArtefact(new ArtefactRefTyped(SdmxArtefactType.CodeLists, "NSI+CL_LAST_UPDATE+1.0")) as CodeListType;
                        if (clt != null && clt.CodeList.Count > 0)
                        {
                            double v = 0.0;
                            foreach (CodeType c in clt.CodeList)
                            {
                                double vc = double.Parse(c.Id.Replace("_", "."));
                                if (vc > v)
                                    v = vc;
                            }
                            lastVers = v.ToString("0.0");
                        }
                    }
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to fetch metadata from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
                if (statusMsg.IsStatusMessage)
                {
                    throw new ApplicationException("Unable to fetch metadata from registry." + Environment.NewLine + "Reason: " + statusMsg.Message);
                }
            }

            return lastVers;
        }

        public XmlDocument GetArtefact(string arType, string fullIdent)
        {
            XmlDocument xDoc = null;

            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.GetArtefactOS(UserId, VS, arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                    if (statusMsg.IsStatusMessage == false)
                    {
                        xDoc = new XmlDocument();
                        xDoc.LoadXml(el.OuterXml);
                    }
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to fetch metadata from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
                if (statusMsg.IsStatusMessage)
                {
                    throw new ApplicationException("Unable to fetch metadata from registry." + Environment.NewLine + "Reason: " + statusMsg.Message);
                }
            }

            return xDoc;
        }

        public bool ExistArtefact(string arType, string fullIdent)
        {
            bool exist = false;

            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.ExistArtefact(arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to check metadata in registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to check Artefact existence. Reason: " + statusMsg.Message);
                }
                else
                    exist = statusMsg.Status == StatusType.Success; // sucess if exist, warning if not existing
            }


            return exist;
        }

        public void DeleteArtefact(string arType, string fullIdent)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.DeleteArtefact(UserId, VS, arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to delete metadata in registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to delete Artefact. Reason: " + statusMsg.Message);
                }
            }
        }

        public void SetDisseminationStatus(string artType, string fullId, string diffusionCd)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.SetDisseminationStatus(UserId, VS, artType, fullId, diffusionCd);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to set diffusion status to registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(artType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullId).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to set diffusion status to registry. Reason: " + statusMsg.Message);
                }
            }
        }

        #region DEC update 17.08.2011 : Implement method to bacup artefact before submission
        public void SubmitArtefactBackup(string arType, string fullIdent, XmlElement element)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.SubmitArtefactBackup(UserId, VS, arType, fullIdent, element);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to submit metadata to registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to submit artefact. Reason: " + statusMsg.Message);
                }
            }
        }
        #endregion

        public void SubmitArtefact(string arType, string fullIdent, XmlElement element)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.SubmitArtefact(UserId, VS, arType, fullIdent, element);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to submit metadata to registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to submit artefact. Reason: " + statusMsg.Message);
                }
            }
        }

        public void CheckOut(string arType, string fullIdent)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.CheckOut(UserId, VS, arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to check out artefact in registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to lock artefact in registry ! : " + statusMsg.Message);
                }
            }
        }

        public void UndoCheckOut(string arType, string fullIdent)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.UndoCheckOut(UserId, VS, arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to unlock artefact in registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to check out artefact in registry ! : " + statusMsg.Message);
                }

            }
        }

        /// <summary>Retrieves information list of artefact for specified type </summary>
        /// <param name="arType">Artefact type</param>
        /// <returns>A dataset holding the information table</returns>
        public System.Data.DataSet DirInfo(string arType)
        {
            System.Data.DataSet ds = new System.Data.DataSet();
            if (State == RepositoryState.Ok)
            {
                try
                {
                    ds = smsProxy.DirInfo(arType);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to obtain metadata list from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
            }

            return ds;
        }

        /// <summary>Retrieves information list of artefact for specified type </summary>
        /// <param name="arType">Artefact type</param>
        /// <returns>A dataset holding the information table</returns>
        public System.Data.DataSet GetArtefactInfos(string artTypePattern, string agencyPattern, string idPattern, string versionPattern, DateTime start, DateTime end)
        {
            System.Data.DataSet ds = new System.Data.DataSet();
            if (State == RepositoryState.Ok)
            {
                try
                {
                    ds = smsProxy.GetArtefactInfos(artTypePattern, agencyPattern, idPattern, versionPattern, start, end);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to obtain metadata list from registry.").Append(Environment.NewLine);
                    sb.Append("Params: ").Append(artTypePattern).Append(", ").Append(agencyPattern).Append(", ").Append(idPattern).Append(", ").Append(versionPattern).Append(", ").Append(start.ToString()).Append(", ").Append(end.ToString()).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
            }

            return ds;
        }

        #region DEC 10.08.2011 : Add information on backup version

        /// <summary>Determines if backup info exists</summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <returns>True if exists</returns>
        public bool IsBackupExist(string ar, string fullId)
        {
            bool bReturn = false;
            DataSet ds = GetBackupList(ar, fullId);

            if (ds != null && ds.Tables.Count == 1 && ds.Tables[0].Rows.Count > 0)
                bReturn = true;

            return (bReturn);
        }

        /// <summary> Get list of backup of specified artefact</summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <returns>A dataset holding backup list information</returns>
        public DataSet GetBackupList(string ar, string fullId)
        {
            DataSet dsReturn = new DataSet();

            if (State == RepositoryState.Ok)
            {
                try
                {
                    dsReturn = smsProxy.GetArtefactBackupList(ar, fullId);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to obtain metadata list from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(ar).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
            }

            return (dsReturn);
        }

        /// <summary> Retrieve a backuped Artefact document </summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <param name="versionNumber">Backup number</param>
        /// <returns></returns>
        public XmlDocument GetArtefactBackup(string ar, string fullId, string versionNumber)
        {
            XmlDocument xDoc = null;

            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.GetArtefactBackup(UserId,
                        VS,
                        ar,
                        fullId,
                        versionNumber);

                    statusMsg = new StatusMessageType(el);
                    if (statusMsg.IsStatusMessage == false)
                    {
                        xDoc = new XmlDocument();
                        xDoc.LoadXml(el.OuterXml);
                    }
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to fetch metadata from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(ar).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullId).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
                if (statusMsg.IsStatusMessage)
                {
                    throw new ApplicationException("Unable to fetch metadata from registry." + Environment.NewLine + "Reason: " + statusMsg.Message);
                }
            }

            return xDoc;
        }
        #endregion

        /// <summary>
        /// Is window used a repository administrator
        /// </summary>
        /// <returns>True id administrator</returns>
        public bool IsAdministrator() { return IsAdministrator(UserId); }
        public bool IsAdministrator(string uid)
        {
            bool isAdmin = false;

            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.IsAdministrator(uid, VS);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to get information about administrative user").Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to get information about administrative user. Reason: " + statusMsg.Message);
                }
                else
                    isAdmin = statusMsg.Status == StatusType.Success && statusMsg.Message.GetFirst().ToLower() == "true";
            }


            return isAdmin;
        }

        public bool IsOwner(string uid, string fullId)
        {
            bool isOwner = false;

            if (State == RepositoryState.Ok && Version >= 1.6)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.IsOwner(uid, VS, fullId);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to get information about ownership").Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    State = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to get information about ownership. Reason: " + statusMsg.Message);
                }
                else
                    isOwner = statusMsg.Status == StatusType.Success && statusMsg.Message.GetFirst().ToLower() == "is owner";
            }


            return isOwner;
        }


        #endregion

        /// <summary> Update text part of the specified finalized maintenable artefact </summary>
        /// <param name="arType">Type of artefact</param>
        /// <param name="fullIdent">AgencyId + Id + version</param>
        /// <param name="xDoc">the maintenable artefact sdmx document</param>
        public void UpdateFinalizedArtefact(string arType, string fullIdent, XmlDocument xDoc)
        {
            StatusMessageType statusMsg = new StatusMessageType();
            try
            {
                string ArtefactUtilityUrn = string.Empty;
                if (ConfigurationManager.AppSettings["UtilityUrl"] != null &&
                    ConfigurationManager.AppSettings["UtilityUrl"].ToString().Trim() != string.Empty)
                    ArtefactUtilityUrn = ConfigurationManager.AppSettings["UtilityUrl"].ToString().Trim();

                if (ArtefactUtilityUrn.Length > 0)
                {

                    SdmxMl.ArtefactUtility.ArtefactUtility wsUtil = new SdmxMl.ArtefactUtility.ArtefactUtility();
                    wsUtil.Url = ArtefactUtilityUrn;
                    statusMsg = new StatusMessageType(
                        wsUtil.UpdateFinalized(UserId, VS, arType, fullIdent, xDoc.DocumentElement));
                }
            }
            catch (Exception ex)
            {
                StringBuilder sb = new StringBuilder(256);
                sb.Append("Unable to Update text of artefact in registry.").Append(Environment.NewLine);
                sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                sb.Append("Reason: ").Append(ex.Message);

                throw new ApplicationException(sb.ToString(), ex);
            }

            if (statusMsg.Status == StatusType.Failure)
            {
                throw new ApplicationException("Unable to Update text of artefact in registry. Reason: " + statusMsg.Message);
            }

        }

    }
}
